package org.strieber.fca;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class ResultsTrimmer {

	private List<Rule> rules = new ArrayList<Rule>();

	// create new ResultsTrimmer from contents of file
	public ResultsTrimmer(File file) throws FileNotFoundException {
		Scanner scanner = new Scanner(file);
		while (scanner.hasNext()) {
			processLine(scanner.nextLine());
		}
	}	
	// process line from file (used internally)
	private void processLine(String line) {
		Scanner scanner = new Scanner(line);
		scanner.useDelimiter("\t");
		String intentsString = "";
		String extentsString = "";
		String supportString = "";
		String confidenceString = "";
		for (int tokenNumber = 0; tokenNumber < 6; tokenNumber++) {
			if (tokenNumber == 0) {
				// throw away rule number
				scanner.next();
			}
			if (tokenNumber == 1) {
				// list of extents
				extentsString = scanner.next();
			}
			if (tokenNumber == 2) {
				// throw away arrow
				scanner.next();
			}
			else if (tokenNumber == 3) {
				// list of intents
				intentsString = scanner.next();
			}
			else if (tokenNumber == 4) {
				// support level
				supportString = scanner.next();
			}
			else if (tokenNumber == 5) {
				// confidence level
				confidenceString = scanner.next();
			}
		}
		
		List<String> extents = processXTent(extentsString);
		List<String> intents = processXTent(intentsString);
		
		double support = 
			Double.parseDouble(removePercent(supportString));
		double confidence = 
			Double.parseDouble(removePercent(confidenceString));
		
		Rule newRule = new Rule(extents, intents, support, confidence);
		rules.add(newRule);
	}
	// remove percent sign from support or confidence (used internally)
	private String removePercent(String string) {
		return string.substring(0, string.length() - 1);
	}
	// process an intent or extent string
	private List<String> processXTent(String xTent) {
		List<String> result = new ArrayList<String>();
		String removedBraces = xTent.substring(1, xTent.length() - 1);
		Scanner scanner = new Scanner(removedBraces);
		scanner.useDelimiter(", ");
		while (scanner.hasNext()) {
			result.add(scanner.next());
		}
		return result;
	}
	// prune rules that do not meet minimum support
	public void pruneByMinSupport(double minSupport) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			double support = rules.get(i).getSupport();
			if (support < minSupport) {
				rules.remove(i);
			}
		}
	}
	// prune rules that exceed maximum support
	public void pruneByMaxSupport(double maxSupport) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			double support = rules.get(i).getSupport();
			if (support > maxSupport) {
				rules.remove(i);
			}
		}
	}
	// prune rules that do not meet minimum confidence
	public void pruneByMinConfidence(double minConfidence) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			double confidence = rules.get(i).getConfidence();
			if (confidence < minConfidence) {
				rules.remove(i);
			}
		}
	}
	// prune rules that exceed maximum confidence
	public void prunebyMaxConfidence(double maxConfidence) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			double confidence = rules.get(i).getConfidence();
			if (confidence > maxConfidence) {
				rules.remove(i);
			}
		}
	}
	// prune rules that do not contain the required exact extent
	public void pruneByRequiredExactExtent(String extent) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			List<String> extents = rules.get(i).getExtents();
			int extentsSize = extents.size();
			boolean keep = false;
			for (int j = 0; j < extentsSize; j++) {
				if (extents.get(j).equals(extent)) {
					keep = true;
				}
			}
			if (keep == false) {
				rules.remove(i);
			}
		}
	}
	// prune rules that do not contain the required exact intent
	public void pruneByRequiredExactIntent(String intent) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			List<String> intents = rules.get(i).getIntents();
			int intentsSize = intents.size();
			boolean keep = false;
			for (int j = 0; j < intentsSize; j++) {
				if (intents.get(j).equals(intent)) {
					keep = true;
				}
			}
			if (keep == false) {
				rules.remove(i);
			}
		}
	}
	// prune rules that do not contain the required extent with
	// any numeric amount on the right side of the equals sign
	public void pruneByRequiredExtent(String extent) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			List<String> extents = rules.get(i).getExtents();
			int extentsSize = extents.size();
			boolean keep = false;
			for (int j = 0; j < extentsSize; j++) {
				if (extents.get(j).contains(extent)) {
					keep = true;
				}
			}
			if (keep == false) {
				rules.remove(i);
			}
		}
	}
	// prune rules that do not contain the required intent with
	// any numeric amount on the right side of the equals sign
	public void pruneByRequiredIntent(String intent) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			List<String> intents = rules.get(i).getIntents();
			int intentsSize = intents.size();
			boolean keep = false;
			for (int j = 0; j < intentsSize; j++) {
				if (intents.get(j).contains(intent)) {
					keep = true;
				}
			}
			if (keep == false) {
				rules.remove(i);
			}
		}
	}
	// prune rules that don't have the required amount of extents
	public void pruneByExactNumberOfExtents(int requiredNumberOfExtents) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			int numberOfExtents = rules.get(i).getExtents().size();
			if (numberOfExtents != requiredNumberOfExtents) {
				rules.remove(i);
			}
		}
	}
	
	// prune rules that don't have the required amount of intents
	public void pruneByExactNumberOfIntents(int requiredNumberOfIntents) {
		int lastRule = rules.size() - 1;
		for (int i = lastRule; i >= 0; i--) {
			int numberOfIntents = rules.get(i).getIntents().size();
			if (numberOfIntents != requiredNumberOfIntents) {
				rules.remove(i);
			}
		}
	}
	
	// print all rules for debugging
	public void printAllRules() {
		int rulesSize = rules.size();
		for (int i = 0; i < rulesSize; i++) {
			System.out.println(rules.get(i));
		}
	}
	// return rules
	public List<Rule> getRules() {
		return rules;
	}
	
	// return rules as list of Strings
	public List<String> getRulesAsStrings() {
		List<String> rulesAsStrings = new ArrayList<String>();
		int rulesSize = rules.size();
		for (int i = 0; i < rulesSize; i++) {
			rulesAsStrings.add(rules.get(i).cleanToString());
		}
		return rulesAsStrings;
	}
	
	// export rules to file
	public void exportRules(File file) throws IOException {
		StringBuilder currentLine = new StringBuilder();
		BufferedWriter out = new BufferedWriter(new FileWriter(file, false)); 
		int rulesSize = rules.size();
		for (int i = 0; i < rulesSize; i++) {
			Rule currentRule = rules.get(i);
			currentLine.append(
					currentRule.getExtents() + "\t=>\t" +
					currentRule.getIntents() + "\t" +
					"S: " + currentRule.getSupport() + "%\t" +
					"C: " + currentRule.getConfidence() + "%" + "\n"
					);
			// avoid extra new line at end of each document
			out.write(currentLine.toString());
			// ready the string builder for the next line
			currentLine.delete(0, currentLine.length());
		}
		out.close();
	}
	// debugging
	public static void main(String[] args) throws Exception {
		String defaultResultsName = "sampleResults.txt";
		String defaultSaveName = "sampleTrimmedResults.txt";
		File file = new File(defaultResultsName);
		ResultsTrimmer rt = new ResultsTrimmer(file);
		rt.pruneByRequiredIntent("CAT");
		rt.pruneByMaxSupport(50);
		File saveFile = new File(defaultSaveName);
		System.out.println("Saving file: " + defaultSaveName);
		rt.exportRules(saveFile);
		rt.printAllRules();
	}
}